package net.liteheaven.xblib.plugin;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Application;
import android.app.Fragment;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.text.TextUtils;

import net.liteheaven.hidefix.ReflectionHelper;

import java.lang.reflect.Method;

/**
 * Instrumentation包装类
 */
public class InstrumentationWrapper extends Instrumentation{

    private Instrumentation instrumentation;

    public InstrumentationWrapper(Instrumentation instrumentation){
        super();
        if (instrumentation == null){
            throw new IllegalArgumentException("instrumentation cannot be null.");
        }
        this.instrumentation = instrumentation;
    }

    @Override
    public boolean onException(Object obj, Throwable e) {
        return instrumentation.onException(obj, e);
    }

    @Override
    public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        Bundle b = intent.getExtras();
        XPluginLog.i("InstrumentationWrapper#newAcitivy className = " + className + " intent extras = " + b);

        if (b != null){
            String pluginActivityName = b.getString(XPluginConst.PLUGIN_ACTIVITY_NAME);
            if (!TextUtils.isEmpty(pluginActivityName)){
                while (true){
                    try{
                        cl.loadClass(pluginActivityName);
                        className = pluginActivityName;
                        break;
                    }catch (ClassNotFoundException e){
                        String pluginName = b.getString(XPluginConst.PLUGIN_NAME);
                        XPlugin plugin = null;
                        if (!TextUtils.isEmpty(pluginName)){
                            try {
                                plugin = PluginKit.getInstance().getManager().get(pluginName);
                            } catch (XPluginException e1) {
                                XPluginLog.i(e1);
                            } catch (Exception e2){
                                XPluginLog.i(e2);
                            }
                        }
                        if (plugin == null){
                            break;
                        }else{
                            cl = plugin.getClassLoader();
                        }
                    }catch (Exception e){
                        XPluginLog.i(e);
                        break;
                    }
                }
            }
        }
        return instrumentation.newActivity(cl, className, intent);
    }

    @Override
    public Activity newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance) throws InstantiationException, IllegalAccessException {
        return instrumentation.newActivity(clazz, context, token, application, intent, info, title, parent, id, lastNonConfigurationInstance);
    }

    @Override
    public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return instrumentation.newApplication(cl, className, context);
    }

    @Override
    public void callActivityOnCreate(Activity activity, Bundle icicle) {
        instrumentation.callActivityOnCreate(activity, icicle);
    }

    @Override
    public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            instrumentation.callActivityOnCreate(activity, icicle, persistentState);
        }
    }

    @Override
    public void callActivityOnDestroy(Activity activity) {
        instrumentation.callActivityOnDestroy(activity);
    }

    @Override
    public void callActivityOnNewIntent(Activity activity, Intent intent) {
        instrumentation.callActivityOnNewIntent(activity, intent);
    }

    @Override
    public void callActivityOnPause(Activity activity) {
        instrumentation.callActivityOnPause(activity);
    }

    @Override
    public void callActivityOnPostCreate(Activity activity, Bundle icicle) {
        instrumentation.callActivityOnPostCreate(activity, icicle);
    }

    @Override
    public void callActivityOnPostCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            instrumentation.callActivityOnPostCreate(activity, icicle, persistentState);
        }
    }

    @Override
    public void callActivityOnRestart(Activity activity) {
        instrumentation.callActivityOnRestart(activity);
    }

    @Override
    public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) {
        instrumentation.callActivityOnRestoreInstanceState(activity, savedInstanceState);
    }

    @Override
    public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState, PersistableBundle persistentState) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            instrumentation.callActivityOnRestoreInstanceState(activity, savedInstanceState, persistentState);
        }
    }

    @Override
    public void callActivityOnResume(Activity activity) {
        instrumentation.callActivityOnResume(activity);
    }

    @Override
    public void callActivityOnSaveInstanceState(Activity activity, Bundle outState) {
        instrumentation.callActivityOnSaveInstanceState(activity, outState);
    }

    @Override
    public void callActivityOnSaveInstanceState(Activity activity, Bundle outState, PersistableBundle outPersistentState) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            instrumentation.callActivityOnSaveInstanceState(activity, outState, outPersistentState);
        }
    }

    @Override
    public void callActivityOnStart(Activity activity) {
        instrumentation.callActivityOnStart(activity);
    }

    @Override
    public void callActivityOnStop(Activity activity) {
        instrumentation.callActivityOnStop(activity);
    }

    @Override
    public void callActivityOnUserLeaving(Activity activity) {
        instrumentation.callActivityOnUserLeaving(activity);
    }

    @Override
    public void callApplicationOnCreate(Application app) {
        instrumentation.callApplicationOnCreate(app);
    }

    // API15
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode) {
        try {
            Method execStartActivity = ReflectionHelper.getDeclaredMethod(Instrumentation.class,
                    "execStartActivity",
                    Context.class, IBinder.class, IBinder.class, Activity.class,
                    Intent.class, int.class);
            execStartActivity.setAccessible(true);

            PluginUtil.handleOutwardIntent(who, intent);

            return (ActivityResult) execStartActivity.invoke(instrumentation, who,
                    contextThread, token, target, intent, requestCode);
        } catch (Exception e) {
            throw new RuntimeException("android version not supported");
        }
    }

    // API15
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Fragment target,
            Intent intent, int requestCode) {
        try {
            Method execStartActivity = ReflectionHelper.getDeclaredMethod(Instrumentation.class,
                    "execStartActivity",
                    Context.class, IBinder.class, IBinder.class, Fragment.class,
                    Intent.class, int.class);
            execStartActivity.setAccessible(true);

            PluginUtil.handleOutwardIntent(who, intent);

            return (ActivityResult) execStartActivity.invoke(instrumentation, who,
                    contextThread, token, target, intent, requestCode);
        } catch (Exception e) {
            throw new RuntimeException("android version not supported");
        }
    }

    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token,
                                            Activity target, Intent intent, int requestCode,
                                            Bundle options) {
        try {
            Method execStartActivity = ReflectionHelper.getDeclaredMethod(Instrumentation.class,
                    "execStartActivity",
                    Context.class, IBinder.class, IBinder.class, Activity.class,
                    Intent.class, int.class, Bundle.class);
            execStartActivity.setAccessible(true);

            PluginUtil.handleOutwardIntent(who, intent);

            return (ActivityResult) execStartActivity.invoke(instrumentation, who,
                    contextThread, token, target, intent, requestCode, options);
        } catch (Exception e) {
            throw new RuntimeException("android version not supported");
        }
    }

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Fragment target,
            Intent intent, int requestCode, Bundle options){
        try {
            Method execStartActivity = ReflectionHelper.getDeclaredMethod(Instrumentation.class,
                    "execStartActivity",
                    Context.class, IBinder.class, IBinder.class, Fragment.class,
                    Intent.class, int.class, Bundle.class);
            execStartActivity.setAccessible(true);

            PluginUtil.handleOutwardIntent(who, intent);

            return (ActivityResult) execStartActivity.invoke(instrumentation, who,
                    contextThread, token, target, intent, requestCode, options);
        } catch (Exception e) {
            throw new RuntimeException("android version not supported");
        }
    }

    @SuppressLint("NewApi")
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options, UserHandle user){
        try {
            Method execStartActivity = null;
            execStartActivity = ReflectionHelper.getDeclaredMethod(Instrumentation.class,
                    "execStartActivity",
                    Context.class, IBinder.class, IBinder.class, Activity.class,
                    Intent.class, int.class, Bundle.class, UserHandle.class);
            execStartActivity.setAccessible(true);

            PluginUtil.handleOutwardIntent(who, intent);

            return (ActivityResult) execStartActivity.invoke(instrumentation, who,
                    contextThread, token, target, intent, requestCode, options, user);
        } catch (Exception e) {
            throw new RuntimeException("android version not supported");
        }
    }
}
